﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MattDotson.GlobalToolkit
{
    /// <summary>
    /// Information about the currency.
    /// </summary>
    [Serializable]
    public sealed class CurrencyInfo :  IEquatable<CurrencyInfo>
    {
        /// <summary>
        /// Gets an instance of <see cref="CurrencyInfo"/> representing the specified <paramref name="isoCode"/>.  This method
        /// uses the <see cref="ICurrencyProvider"/> returned from <see cref="GlobalToolkitContext.ServiceLocator"/> to
        /// lookup the currency information.
        /// </summary>
        /// <param name="isoCode">The iso code.</param>
        /// <returns>An instance of <see cref="CurrencyInfo"/> for the currency specified by <paramref name="isoCode"/></returns>
        public static CurrencyInfo GetCurrencyInfo(string isoCode)
        {
            Guard.NotNullOrEmpty("isoCode", isoCode);

            ICurrencyProvider provider = GlobalToolkitContext.ServiceLocator.Resolve<ICurrencyProvider>();
            if (null == provider)
            {
                //throw an exception if a currency provider has not been configured.
                //todo: message from resources
                throw new CurrencyException();
            }

            return provider.GetCurrencyInfo(isoCode);
        }

        /// <summary>
        /// Gets instances of <see cref="CurrencyInfo"/> representing all known currencies.  This method
        /// uses the <see cref="ICurrencyProvider"/> returned from <see cref="GlobalToolkitContext.ServiceLocator"/> to
        /// lookup the currency information.  This provider can be overridden with your own currency provider.
        /// </summary>
        /// <returns>A set of all known currencies.</returns>
        public static IEnumerable<CurrencyInfo> GetCurrencies()
        {
            ICurrencyProvider provider = GlobalToolkitContext.ServiceLocator.Resolve<ICurrencyProvider>();
            if (null == provider)
            {
                //throw an exception if a currency provider has not been configured.
                //todo: message from resources
                throw new CurrencyException();
            }

            return provider.GetCurrencies();
        }

        /// <summary>
        /// Gets the three letter ISO 4217 code
        /// </summary>
        /// <value>The iso currency code.</value>
        public string IsoCurrencyCode { get; private set; } 

        /// <summary>
        /// Gets or sets the local currency symbol (i.e. $).
        /// </summary>
        /// <value>The symbol.</value>
        public string Symbol { get; private set; }

        /// <summary>
        /// Gets or sets The number of decimal places common to the currency.
        /// </summary>
        /// <value>The decimal digits.</value>
        public int DecimalDigits { get; private set; } 

        /// <summary>
        /// Gets or sets the name of the currency.
        /// </summary>
        /// <value>The name.</value>
        public string Name { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CurrencyInfo"/> class.
        /// </summary>
        /// <param name="isoCode">The 4217 ISO code of the currency.</param>
        /// <param name="name">The name.</param>
        /// <param name="symbol">The symbol.</param>
        /// <param name="decimalDigits">The decimal digits.</param>
        public CurrencyInfo(string isoCode, string name, string symbol, int decimalDigits)
        {
            Guard.NotNullOrEmpty("isoCode", isoCode);
            Guard.NotNullOrEmpty("name", name);
            Guard.NotNullOrEmpty("symbol", symbol);

            this.IsoCurrencyCode = isoCode;
            this.Name = name;
            this.Symbol = symbol;
            this.DecimalDigits = decimalDigits;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            CurrencyInfo val = obj as CurrencyInfo;
            return (null != val) 
                && String.Equals(this.IsoCurrencyCode, val.IsoCurrencyCode, StringComparison.Ordinal);
        }

        /// <summary>
        /// Returns the hash code for this instance.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that is the hash code for this instance.
        /// </returns>
        public override int GetHashCode()
        {
            return this.IsoCurrencyCode.GetHashCode();
        }

        #region IEquatable<CurrencyInfo> Members

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        bool IEquatable<CurrencyInfo>.Equals(CurrencyInfo other)
        {
            return this.Equals((object)other);
        }

        #endregion
    }
}
